
(function() {
	var ICON_WIDTH = 18;

	/**
	 * Widget of Treeview, can be linked to a TreeNode instance.
	 *
	 * @namespace Widget
	 * @class TreeView
	 */
	var TreeView = G.GClass('Widget', 'TreeView', G.Base.EventListener);

	/**
	 * Constructure of TreeView.
	 * @method __init
	 * @param data {TreeNode} the data represent the tree.
	 */
	TreeView.prototype.__init = function(data) 
	{
		this.__Base__EventListener__init();
		this.root = null;
		this.data = data || null;
		this.nodeByPath = {};
		this.container = null;
		this.gContainer = null;

		this._drawRoot = false;
		this.currentPath = null;
	}

	/**
	 * Load the data that represent the tree structure. If the container is already 
	 * specified, the tree will be rendered in it.
	 *
	 * @method link_data
	 * @param data {TreeNode} the data represent the tree.
	 */
	TreeView.prototype.link_data = function(data, get_title, get_class) 
	{
		this.data = data;
		this.get_title = get_title || function(node) {return node.id};
		this.get_class = get_class || function(node) {return ''};
		if (this.container) {
			this.clear();
			if (this._drawRoot) {
				this.__render(data, this.container, null);
			} else {
				for (var n in data.childNodes) {
					this.__render(data.childNodes[n], this.container, null);
				}
			}
		}
	}

	/**
	 * Clear all elements in the container.
	 * @method clear
	 */
	TreeView.prototype.clear = function() {
		this.container.innerHTML = "";
	}

	/**
	 * Render the tree in the container
	 * @method render
	 */
	TreeView.prototype.render = function(container) {
		if (typeof(container) == 'string') {
			container = document.getElementById(container);
		}
		this.container = container;
		this.container.innerHTML = "";
		container.className = 'treeview';
		this.gContainer = G(this.container);
		this.gContainer.bind("onclick", this.__on_click, this);
		if (this.data) {
			this.__render(this.data, this.container, null);
		}
	}

	TreeView.prototype.__render = function(treeNode, container, before) 
	{ 
		var title = this.get_title(treeNode);
		var classname = this.get_class(treeNode);

		var li = document.createElement('li');
		var leader = document.createElement('div');
		var a = document.createElement('a');
		li.name = treeNode.get_fullname();
		li.appendChild(leader);
		li.appendChild(a);

		a.innerHTML = title;
		if (classname) {
			a.className = classname;
		}
	
		container.insertBefore(li, before);
		var subtree = document.createElement('div');
		container.insertBefore(subtree, before);
		if (treeNode.get_child_count() > 0) {
			leader.className = "unfolder";
		}
		
		this.nodeByPath[treeNode.get_fullname()] = {
			'li': li, 'subtree': subtree, 'data': treeNode 
		}

		for (var n in treeNode.childNodes) {
			this.__render(treeNode.childNodes[n], subtree, null);
		}
	}

	/**
	 * Add a sub tree under the node in given path.
	 *
	 * @method add_node
	 * @param father {string} the path of the father node.
	 * @param data {TreeNode} the tree node data
	 * @param before {string}
	 * @data 
	 */

	TreeView.prototype.insert_child = function(father, data, before) {
		var fnode = this.nodeByPath[father];

		if (!fnode)		return null;
		var container = fnode.subtree;

		if (typeof(before) != 'undefined') {
			before = this.nodeByPath[before] || null;
			this.__render(data, fnode.container, before);
		} else {
			this.__render(data, fnode.container, null);
		}
	}

	/**
	 * Find the node in tree by the given path, and set it as current node.</b>
	 * Note, NO selectchanged EVENT fired on this call</br>
	 * 
	 * @method set_current
	 * @param path {string}
	 */
	TreeView.prototype.set_current = function(path) {

		var li = null;
		var old = this.currentPath;

		if (path == null) { // Remove selection.
			if (this.currentNode) {
				this.currentNode.li.removeAttribute("className");
				this.currentNode.li.removeAttribute("class");
			}
			return true;
		} 

		var n = this.nodeByPath[path] || null;
		if (n == null) {
			return false;
		}

		if (this.currentNode) {
			this.currentNode.li.removeAttribute("className");
			this.currentNode.li.removeAttribute("class");
		}

		this.currentPath = path;
		this.currentNode = n;
		n.li.className = "current";
	}

	/**
	 * Toggle folder of the node refered by the given path
	 * @method toggle_folder
	 */
	TreeView.prototype.toggle_folder = function(path) {

		if (path in this.nodeByPath) {
			n = this.nodeByPath[path];
			this.__toggle_folder(n);
		}
	}

	TreeView.prototype.__toggle_folder = function(node) {
		var leader = node.li.childNodes[0];
		if (leader.className == 'unfolder') {
			if (node.subtree) {
				node.subtree.className = 'folder';
			}
			leader.className = 'folder';
		} else if (leader.className == 'folder') {
			if (node.subtree) {
				node.subtree.className = 'unfolder';
			}
			leader.className = 'unfolder';
		} else {
			return false;
		}
		return true;
	}

	TreeView.prototype.__on_click = function(ev) {
		var target = ev.srcElement;
		var li = target.parentNode;
		if (li.tagName != "LI")	return;

		var n = this.nodeByPath[li.name];
		if (target.tagName == 'DIV') {
			if (this.__toggle_folder(n)) {
				this.fire("onfoldertoggle", n);
			}
		} else if (target.tagName == 'A') {
			if (this.currentPath != li.name) {
				var old = this.currentPath;
				this.set_current(li.name);
				this.fire("onselectchanged", {old: old, current: li.name, node: n.data});
			}
		}
	}


	TreeView.prototype.get_current_data = function() {
		if (this.currentNode == null)	return null;
		return this.currentNode.data.data;
	}
	TreeView.prototype.get_current_path = function() {
		return this.currentPath;
	}
	TreeView.prototype.get_current_node = function() {
		return this.currentNode;
	}

	G.DOM.Element.prototype.TreeView = function(data) {
		var tv = new TreeView(data);
		tv.render(this.em);
		return tv;
	}

})();
